#pragma once


namespace micro
{
namespace utils
{



class QBaseArray
{
protected:
    int			m_capacity;
    int			m_size;
    void 	*	m_items;
    size_t		m_itemSize;



    void MoveItems( int destIndex, int srcIndex )
    {
        memmove( ( ( unsigned char * )m_items ) + destIndex * m_itemSize, ( ( unsigned char * )m_items ) + srcIndex * m_itemSize, m_itemSize * ( m_size - srcIndex ) );
    }

    void InsertOneItem( int index )
    {
        ReserveOnePosition();
        MoveItems( index + 1, index );
        m_size++;
    }



    //bytes
    void TestIndexAndCorrectNum( int index, int & num ) const
    {
        if ( index + num > m_size )
            num = m_size - index;
    }



public:
    QBaseArray( size_t itemSize ): m_capacity( 0 ), m_size( 0 ), m_items( 0 ), m_itemSize( itemSize ) {}
    ~QBaseArray()
    {
        if ( m_items )
            delete []( ( unsigned char * )m_items );
    }


    inline int GetSize() const
    {
        return m_size;
    }
    inline bool IsEmpty() const
    {
        return ( m_size == 0 );
    }
    void DeleteBack()
    {
        Remove( m_size - 1 );
    }
    void DeleteFrom( int index )
    {
        Remove( index, m_size - index );
    }
    void RemoveAll()
    {
        DeleteFrom( 0 );
    }
    virtual void RemoveAt( int index, int num = 1 )
    {
        Remove( index,  num );
    }
    virtual void Remove( int index, int num = 1 )
    {
        TestIndexAndCorrectNum( index, num );

        if ( num > 0 )
        {
            MoveItems( index, index + num );
            m_size -= num;
        }
    }

    void ReserveOnePosition()
    {
        if ( m_size != m_capacity )
            return;

        int delta;

        if ( m_capacity > 64 )
            delta = m_capacity / 2;
        else if ( m_capacity > 8 )
            delta = 8;
        else
            delta = 4;

        Reserve( m_capacity + delta );
    }


    //items
    void Reserve( int newCapacity )
    {
        if ( newCapacity <= m_capacity )
            return;

        unsigned char * p = new unsigned char[newCapacity * m_itemSize];
        int numsToMove = m_capacity;
		
        memmove( p, m_items, m_itemSize * numsToMove );
        delete []( unsigned char * )m_items;
        m_items = p;
        m_capacity = newCapacity;

    }

};


template <class T>
class QArray: public QBaseArray
{
public:
    QArray(): QBaseArray( sizeof( T ) ) {};

    QArray( const QArray & v ): QBaseArray( sizeof( T ) )
    {
        Add( v );
    }

    int Add( T item )
    {
        ReserveOnePosition();
        ( ( T * )m_items )[m_size] = item;
        return m_size++;
    }

    void Add( const QArray & v )
    {
        int size = v.GetSize();
        Reserve( GetSize() + size );

        for ( int i = 0; i < size; i++ )
            Add( v[i] );

    }

    void Copy( const QArray & v )
    {
        RemoveAll();
        Add( v );
    }



    QArray & operator=( const QArray & v )
    {
        Copy( v );
        return ( *this );
    }
    QArray & operator+=( const QArray & v )
    {
        Add( v );
        return *this;
    }

    void Insert( T item, int index )
    {
        InsertOneItem( index );
        ( ( T * )m_items )[index] = item;
    }

    inline const T & operator[]( int index ) const
    {
        return ( ( T * )m_items )[index];
    }
    inline T & operator[]( int index )
    {
        return ( ( T * )m_items )[index];
    }
    inline	const T & Front() const
    {
        return operator[]( 0 );
    }
    inline T & Front()
    {
        return operator[]( 0 );
    }
    inline const T & Back() const
    {
        return operator[]( m_size - 1 );
    }
    inline T & Back()
    {
        return operator[]( m_size - 1 );
    }

    inline void Swap( int i, int j )
    {
        T temp = operator[]( i );
        operator[]( i ) = operator[]( j );
        operator[]( j ) = temp;
    }

    int Find( const T & item ) const
    {
        int len = GetSize();

        for ( int i = 0; i < len; i++ )
            if ( ( *this )[i] == item )
                return i;

        return -1;
    }

    int FindInSorted( const T & item ) const
    {
        int left = 0, right = GetSize();

        while ( left != right )
        {
            int mid = ( left + right ) / 2;
            const T & midValue = ( *this )[mid];

            if ( item == midValue )
                return mid;

            if ( item < midValue )
                right = mid;
            else
                left = mid + 1;
        }

        return -1;
    }

    void Sort( int left, int right )
    {
        if ( right - left < 2 )
            return;

        Swap( left, ( left + right ) / 2 );
        int last = left;

        for ( int i = left; i < right; i++ )
            if ( operator[]( i ) < operator[]( left ) )
                Swap( ++last, i );

        Swap( left, last );

        Sort( left, last );
        Sort( last + 1, right );
    }


    void Sort()
    {
        Sort( 0, GetSize() );
    }



    void Quicksort( int begin, int end, int ( *compare )( const int, const int, void * ), void * param )
    {
        int pivot, right, left;

        if ( end - begin > 1  )
        {
            pivot = begin;
            left = begin + 1;
            right = end;

            while ( left < right )
            {
                if ( compare( left, pivot, param ) <= 0 )
                    left++;
                else
                    Swap( left ,   --right );
            }

            Swap( --left , begin );

            Quicksort( begin, left, compare, param );
            Quicksort( right, end,  compare, param );
        }
    }


    inline void Sort( int ( *compare )( const int, const int, void * ), void * param )
    {
        Quicksort( 0, GetSize(), compare, param );
    }



    //range for needs this
//     T * begin()
//     {
//         return ( T * )m_items[0];
//     }
//     T * end()
//     {
//
//         return ( T * )m_items[n - 1];
//     }

};




typedef QArray<void *>			QPtrArray;
typedef QArray<int>				QIntArray;
typedef QArray<unsigned int>	QUIntArray;
typedef QArray<bool>			QBoolArray;
typedef QArray<unsigned char>	QByteArray;



//bytes
class QBuffer: public QByteArray
{
public:
    QBuffer() { }
    QBuffer( LPBYTE  buff, int nrItems )
    {
  
        AddBuffer( buff, nrItems );
    }

    void  AddBuffer( LPBYTE  buff, int nrItems )
    {
        Reserve ( m_size + nrItems );
        memcpy( ( LPBYTE )m_items + m_size, buff, nrItems );
        m_size += nrItems;
    }

    //bytes
    int CopyTo( LPBYTE buff, int index, int num )
    {
        TestIndexAndCorrectNum( index, num );
        memcpy( buff, ( void * ) ( (LPBYTE )m_items + index ) , num  );

        return num;
    }

	operator LPBYTE()
	{
		return ( LPBYTE ) m_items;
	}
    operator LPCSTR()
    {
        return ( LPCSTR ) m_items;
    }

    bool LoadResource( LPCTSTR name , LPCTSTR type )
    {
        HMODULE exe = ::GetModuleHandle( nullptr );
        HRSRC hRes = FindResource( exe, name,  type );

        if ( !hRes )
            return false;

        HGLOBAL hResLoad = ::LoadResource( exe, hRes );

        if ( hResLoad == nullptr )
            return false;


        LPBYTE  mem = ( LPBYTE ) LockResource( hResLoad );

        if ( mem == nullptr )
            return false;

        DWORD sizeRes = SizeofResource( exe, hRes );


        AddBuffer( mem, sizeRes );

        return true;
    }
};




template <class T>
class QObArray: public QPtrArray
{
public:
    QObArray() {};
    ~QObArray()
    {
        RemoveAll();
    }


    QObArray( const QObArray & objectVector )
    {
        *this = objectVector;
    }

    void Copy( const QObArray & objectVector )
    {
        RemoveAll();

        operator+= ( objectVector );
    }

    QObArray & operator=( const QObArray & objectVector )
    {
        RemoveAll();
        return ( *this += objectVector );
    }

    QObArray & operator+=( const QObArray & objectVector )
    {
        int size = objectVector.GetSize();
        Reserve( GetSize() + size );

        for ( int i = 0; i < size; i++ )
            Add( objectVector[i] );

        return *this;
    }
    inline  T & GetAtReference( int index )
    {
        return operator[]( index );
    }
    inline const T & GetAtReference( int index )const
    {
        return operator[]( index );
    }
    inline T * GetAtPointer( int index )
    {
        return ( T * )QPtrArray::operator[]( index );
    }

    inline 	const T & operator[]( int index ) const
    {
        return *( ( T * )QPtrArray::operator[]( index ) );
    }

    inline 	T & operator[]( int index )
    {
        return *( ( T * )QPtrArray::operator[]( index ) );
    }


    T & Front()
    {
        return operator[]( 0 );
    }
    const T & Front() const
    {
        return operator[]( 0 );
    }


    T & Back()
    {
        return operator[]( m_size - 1 );
    }
    const T & Back() const
    {
        return operator[]( m_size - 1 );
    }



    int Add( const T & item )
    {
        return QPtrArray::Add( new T( item ) );
    }
    int Add(      T * item )
    {
        return QPtrArray::Add( item );
    }

    void Insert( const T & item, int index )
    {
        QPtrArray::Insert( new T( item ), index );
    }
    void Insert(       T * item, int index )
    {
        QPtrArray::Insert(  item , index );
    }


    virtual void Remove( int index, int num = 1 )
    {
        TestIndexAndCorrectNum( index, num );

        for ( int i = 0; i < num; i++ )
        {
            T * p = ( T * )  ( ( ( void ** ) m_items )[index + i] );

            delete p;
        }

        QPtrArray::Remove( index, num );
    }





    int Find( const T & item ) const
    {
        int nr =  GetSize();

        for ( int i = 0; i < nr; i++ )
        {
            const T  & val = operator[]( i );

            if (  val == item  )
                return i;
        }

        return -1;
    }

    int FindInSorted( const T & item ) const
    {
        int left = 0, right = GetSize();

        while ( left != right )
        {
            int mid = ( left + right ) / 2;
            const T & midValue = ( *this )[mid];

            if ( item == midValue )
                return mid;

            if ( item < midValue )
                right = mid;
            else
                left = mid + 1;

        }

        return -1;
    }



    int AddToSorted( const T & item )
    {
        int left = 0, right = GetSize();

        while ( left != right )
        {
            int mid = ( left + right ) / 2;
            const T & midValue = ( *this )[mid];

            if ( item == midValue )
            {
                right = mid + 1;
                break;
            }

            if ( item < midValue )
                right = mid;
            else
                left = mid + 1;
        }

        Insert( item, right );
        return right;
    }

    int AddToSorted( const T * item )
    {
        return AddToSorted( *item );
    }



    void sort( TCHAR * array[], int low0, int high0 )
    {
        int low = low0;
        int high = high0;

        if ( high0 > low0 )
        {

            wchar_t * mid = array[( low0 + high0 ) / 2];

            while ( low <= high )
            {
                while ( low < high0  && _tcscmp( array[low], mid ) < 0 )
                {
                    ++low;
                }

                while ( high > low0  && _tcscmp( array[high], mid ) > 0 )
                {
                    --high;
                }

                if ( low <= high )
                {
                    wchar_t * t = array[low];
                    array[low]  = array[high];
                    array[high] = t;
                    ++low;
                    --high;
                }
            }

            if ( low0 < high )
            {
                sort( array, low0, high );
            }

            if ( low < high0 )
            {
                sort( array, low, high0 );
            }
        }
    }



    /*
    typedef int (*QCOMPAREFUNC)(const T &, const  T& , void * param );

    void Sort2(int start, int stop,  QCOMPAREFUNC  compare, void* param )
    {
    	if (stop - start < 2)      return;
    	Swap(start, (start + stop) / 2);

    	int last = start;

    	for (int k = start; k < stop; k++)
    	  if ( compare (  (*this)[k],  (*this)[start] , param ) < 0 )
    		  Swap(++last, k);

    	Swap(start, last);
    	Sort(start, last, compare, param);
    	Sort(last + 1, stop, compare, param);
    }*/



    //range for needs this
    T * begin()
    {
        return GetAtPointer( 0 );
    }
    T * end()
    {

        return GetAtPointer( m_size - 1 );
    }

    //void Sort(int (*compare)(void *const *, void *const *, void *), void *param)
    //{ QPtrArray::Sort(compare, param); }


    template <class T>
    friend QArchive & operator<<( QArchive & ar,  const QObArray<T> &   );
    template <class T>
    friend QArchive & operator>>( QArchive & ar,  QObArray<T> &   );



};

typedef QObArray<QString>		QStringArray;


template <class T>
QArchive & operator<<( QArchive & ar,  const QObArray<T> & vec )
{
    int size = vec.GetSize();
    ar << size;

    for ( int i = 0; i < size; i++ )
        ar << vec.GetAtReference( i );

    return ar;
}


template <class T>
QArchive & operator>>( QArchive & ar,  QObArray<T> & vec )
{

    int size = 0;
    ar >> size;

    if ( !size )
        return ar;

    vec.Reserve(  size );

    for ( int i = 0; i < size; i++ )
    {
        T obj;
        ar >> obj;
        vec.Add( obj );
    }

    return ar;
}






template <class T> void Swap( T & a, T & b )
{
    T t = a;
    a = b;
    b = t;
}
template <class T> inline T Min( T a, T b )
{
    return a < b ? a : b;
}
template <class T> inline T Max( T a, T b )
{
    return a > b ? a : b;
}
template <class T> inline int Compare( T a, T b )
{
    return a < b ? -1 : ( a == b ? 0 : 1 );
}


inline int BoolToInt( bool value )
{
    return ( value ? 1 : 0 );
}
inline bool IntToBool( int value )
{
    return ( value != 0 );
}




}
}//micro